-
Notifications
You must be signed in to change notification settings - Fork 156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Finite class #2858
Add Finite class #2858
Conversation
540afcf
to
4d3695c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple of questions! Mostly wondering whether it would make sense to provide an Index
-based Enum
instead.
res = x :> prev' | ||
prev' = case natVal (asNatProxy prev') of | ||
0 -> repeat def | ||
_ -> let next = x +>> prev | ||
in registerB clk rst en (repeat def) next | ||
_ -> let next' = x +>> prev' | ||
in registerB clk rst en (repeat def) next' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fact that you have to do this is a pretty strong hint these names shouldn't be in Clash.Prelude
. Grepping my projects for prev
and next
also reveals a number of uses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's kind of unavoidable. Every new name we choose might already be picked up in existing user code and I doubt that it's in the interest of the user, if we choose artificially unintuitive names just because of that.
So yes, the user might experience some new warnings about "clashing" names after a Clash update, which are always easy to fix via renaming or hiding the new stuff from Clash.Prelude
, in case they are not desired.
Please note that finding a good (and intuitive) naming scheme that doesn't clash with basic existing libraries (in particular base
) is a challenge already, especially if the offered primitives are quire fundamental. I also tried to improve the situation here based on some prior experience I had with the finite library, which comes with a similar interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the point is we write many state machines in Clash and there prev
and next
are very intuitive local names. Of course you can't avoid all name clashes, but there are some words that are very common in Clash code and prev
and next
are two examples of that specific category. So I agree with Martijn that trying to come up with an alternative is worthwhile in this specific case.
[edit]
Also, I believe we felt we wanted to move away from primes as a suffix in our code, instead using numeric suffixes to disambiguate. So I believe it'd be better to do that instead of introducing new uses of prime in our code base.
[/edit]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
before/after?
prior/later?
back/forward?
Did not think them through much, just throwing it out there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fpred
/ fsucc
for finite versions of Enum functions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the Haddock of a class definition is not normative, what is? They usually use the word law in these cases, but there's no wiggle room in the doc that I see. If they wanted to document something that is common but not normative, I believe they would at the least add the word usually.
[edit]
My point is that these sentences are not misleading. Can you point me to an authoritative text that says otherwise? They are laws. They just didn't use the word law.
[/edit]
[edit 2]
satSucc
and satPred
are based on Enum
. Enum
says they should add and subtract one. So the doc for satSucc
and satPred
says they should add and subtract one.
Originally satSucc
and satPred
weren't even part of SaturatingNum
. But we found that this lead to undesired behaviour of specific instances, so we moved them into the class because they're related enough to justify that.
[/edit 2]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My point is that these sentences are not misleading. Can you point me to an authoritative text that says otherwise? They are laws. They just didn't use the word law.
I'm not aware of any authoritative text, but the style of class requirement descriptions in base
and ghc-prim
varies a lot, so it doesn't look to me like they have a strict enforcement on wording at this point. I gave some examples below. According to those you need to take the Applicative
laws very seriously for example, while the equality laws are more kind of a nice-to-have 🙃.
Eq
The Haskell Report defines no laws for Eq. However, == is customarily expected to implement an equivalence relationship where two values comparing equal are indistinguishable by "public" functions, with a "public" function being one not allowing to see implementation details. For example, for a type representing non-normalised natural numbers modulo 100, a "public" function doesn't make the difference between 1 and 201. It is expected to have the following properties:
Ord
The Haskell Report defines no laws for Ord. However, <= is customarily expected to implement a non-strict partial order and have the following properties:
Applicative
Further, any definition must satisfy the following:
Monad
Instances of Monad should satisfy the following:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
satSucc
andsatPred
are based onEnum
.
Sure satSucc
has a default implementation, which is satSucc s n = satAdd s n 1
, but there is no requirement that I cannot chose a different implementation for my own type instances at this point.
It's also a questionable, whether just because of that every class instance should depend on having a Num
instance as well. It would be much more user-friendly to have a default signature that requires Num
, so the constraint is only required if the user wants to use the default in the first place, e.g.,
satSucc :: SaturationMode -> a -> a
-- Default method suitable for types that can represent the number 1
default satSucc :: Num a => SaturationMode -> a -> a
satSucc s n = satAdd s n 1
{-# INLINE satSucc #-}
This would elide the requirement on Num
for every class instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding satSucc
and satPred
I have primarily not been talking about documentation or implementation, I'm talking about invisible things like design and intent. And also how they changed once we noticed deficiencies.
Sure
satSucc
has a default implementation, which issatSucc s n = satAdd s n 1
, but there is no requirement that I cannot chose a different implementation for my own type instances at this point.
We mean our documentation to be normative. So yes, I definitely interpret it as a requirement that they should add/subtract 1.¹ You yourself argued this above:
Do I miss something fundamental here? I read that like
satSucc
is equivalent to(+ 1)
, except for the boundary cases, where the behavior might differ. That's what I consider by "to compatible with each other".
I'd strongly argue that even in the boundary cases the behaviour matches adding/subtracting 1, it's just that (+ 1)
does not actually add 1 in those cases.
(continuing with quote from your latest message)
It's also a questionable, whether just because of that every class instance should depend on having a
Num
instance as well.
I think discussing creating another class for satSucc
and satPred
is out-of-scope here.
¹ Unless you're talking about boundary cases where the default implementation does not actually satisfy the laws, but then I don't understand why you bring it up.
7a1399a
to
1130232
Compare
Another thing to consider: there is at least some overlap with Counter I believe. Perhaps |
9564261
to
80dc978
Compare
Good point. Making How about adding another deriving strategy for |
I think however we slice it this PR will be an API change, unless we decide to not export it from
I think |
decaec3
to
c8821c4
Compare
c8821c4
to
042a148
Compare
cb4795a
to
8aeab59
Compare
👍 [edit]
I never have used |
Okay, when this is merged, could you create an issue that we should add the instance? |
3719e4a
to
de1af6f
Compare
e8cf2ec
to
bbf0415
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had another pass over the Haddock and feel there's still some needed changes.
bbf0415
to
e863bfe
Compare
e863bfe
to
d8561b6
Compare
d8561b6
to
8df17ea
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I strongly feel the functions should have been called fpred
and fsucc
(or finSucc
), but since you've made it clear that this is not up for discussion, I suppose I'll yield and approve.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to neither approve nor deny this PR on the grounds that I strongly feel squatting the names succMaybe
in the Prelude is no bueano. The functions Counter
exposes could just as well have been called this with slightly different design decisions. They should be called finSucc
(in line with satSucc
and countSucc
) or maybe fsucc
.
I propose to switch your point of view, as Thus, using the name |
I don't think I agree with your logic, but more importantly: you are basing your arguments on very subtle details that will be lost on the majority of Clash users. As a result, the API will be less memorable. |
Why do you consider the semantics of a function to be subtle details?
How do you know that? |
Also note that when we add the instance for I really don't feel like participating in this discussion otherwise, but you raise an important point: your laws prohibit compatibility with our existing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The laws are contradictory. They state predMaybe
and succMaybe
should enumerate all values, and simultaneously that they need to be compatible with Enum
. Our Fixed
type will break this as it can not satisfy both. It is also an example of how they are overconstrained. The Enum
compatibility law probably needs to be removed; the existing laws should already fix the behaviour of predMaybe
and succMaybe
.
It looks to me like the The current For all four numeric types (which are only For any other type the semantics is: Class The sequential order of
and, thus, the successor of As enumFromThen x y = enumFromThenTo x y bound
where
bound | fromEnum y >= fromEnum x = maxBound
| otherwise = minBound which does not hold for
|
Yeah, we're not going to change Also, you're missing the bigger point, it doesn't matter what Finally, if they really wanted [edit] [edit 2]
What exactly do you foresee your additional requirement
to add? Can you think of an example of a |
That's not an issue of this PR. It's not conform with
This is only true for
Why not? Every type that doesn't satisfy this requirement simply is not eligible to be an instance of
Because you requested changes with the assumption that the laws are contradictory. However, I don't see anything contradicting. If a type cannot satisfy the laws, it simply has no
The user shall be able to expect that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a type cannot satisfy the laws, it simply has no
Finite
instance.
I don't agree and I am way beyond done with all the discussion in this PR.
Unless you remove the Enum
compatibility law, I do not want to approve this PR. I'm also still quite conflicted about the naming.
I understand. The Enum compatibility law is an essential part of the intended design. Thank you all for the feedback. |
The PR adds the
Finite
class as well as supplemental instances for most of the standard types.Finite
is a class for types with only finitely many inhabitants and can be considered a more hardware-friendly alternative toBounded
andEnum
, utilizingIndex
instead ofInt
and vectors instead of lists.Have a look at the haddock documentation for further insights.
Requires
Still TODO: